
using System;
using System.Collections.Generic;
using System.Text;
using ScrewTurn.Wiki.PluginFramework;

namespace ScrewTurn.Wiki {

	/// <summary>
	/// Contains the Navigation Paths.
	/// </summary>
	public class NavigationPaths {

		private static NavigationPaths instance;

		/// <summary>
		/// Gets or sets the singleton instance of the <b>NavigationPaths</b> object.
		/// </summary>
		public static NavigationPaths Instance {
			get {
				if(instance == null) throw new InvalidOperationException("NavigationPaths.Instance is null");
				return instance;
			}
			set { instance = value; }
		}

		private List<NavigationPath> paths; // Always sorted

		/// <summary>
		/// Initializes a new instance of the <b>NavigationPaths</b> class.
		/// </summary>
		public NavigationPaths() {
			paths = new List<NavigationPath>();
			Load();
		}

		/// <summary>
		/// Loads all the Navigation Paths.
		/// </summary>
		public void Load() {
			lock(paths) {
				paths.Clear();
				for(int i = 0; i < Collectors.PagesProviderCollector.AllProviders.Length; i++) {
					LoadFrom(Collectors.PagesProviderCollector.AllProviders[i], false);
				}
				paths.Sort(new NavigationPathComparer());
			}
		}

		/// <summary>
		/// Loads the Navigation Paths from a Provider.
		/// </summary>
		/// <param name="provider">The Provider.</param>
		/// <param name="autoSort">True to sort the list.</param>
		public void LoadFrom(IPagesStorageProvider provider, bool autoSort) {
			paths.AddRange(provider.AllNavigationPaths);
			if(autoSort) paths.Sort(new NavigationPathComparer());
		}

		/// <summary>
		/// Reloads the Navigation Paths from a Provider.
		/// </summary>
		/// <param name="provider">The Provider.</param>
		/// <param name="autoSort">True to sort the list.</param>
		public void ReloadFrom(IPagesStorageProvider provider, bool autoSort) {
			UnloadFrom(provider);
			LoadFrom(provider, autoSort);
		}

		/// <summary>
		/// Unloads Navigation Paths managed by a Provider.
		/// </summary>
		/// <param name="provider">The Provider.</param>
		public void UnloadFrom(IPagesStorageProvider provider) {
			List<NavigationPath> toRemove = new List<NavigationPath>();
			for(int i = 0; i < paths.Count; i++) {
				if(paths[i].Provider == provider) {
					toRemove.Add(paths[i]);
				}
			}
			for(int i = 0; i < toRemove.Count; i++) {
				paths.Remove(toRemove[i]);
			}
		}

		/// <summary>
		/// Gets the list of the Navigation Paths.
		/// </summary>
		public List<NavigationPath> Paths {
			get { return paths; }
		}

		/// <summary>
		/// Finds a Navigation Path's Name.
		/// </summary>
		/// <param name="name">The Name.</param>
		/// <returns>True if the Navigation Path exists.</returns>
		public bool Exists(string name) {
			return Find(name) != null;
		}

		/// <summary>
		/// Finds and returns a Path.
		/// </summary>
		/// <param name="name">The Name.</param>
		/// <returns>The Navigation Path or null.</returns>
		public NavigationPath Find(string name) {
			int idx = paths.BinarySearch(new NavigationPath(name, null), new NavigationPathComparer());
			if(idx >= 0) return paths[idx];
			else return null;
		}

		/// <summary>
		/// Adds a new Navigation Path.
		/// </summary>
		/// <param name="name">The Name.</param>
		/// <param name="pages">The Pages.</param>
		/// <param name="provider">The Provider to use for the new Navigation Path.</param>
		/// <returns>True if the Path is added successfully.</returns>
		public bool AddPath(string name, List<PageInfo> pages, IPagesStorageProvider provider) {
			if(Exists(name)) return false;

			if(provider == null) provider = Collectors.PagesProviderCollector.GetProvider(Settings.DefaultPagesProvider);

			NavigationPath newPath = provider.AddNavigationPath(name, pages.ToArray());
			if(newPath != null) {
				lock(paths) {
					paths.Add(newPath);
					paths.Sort(new NavigationPathComparer());
				}
				Log.LogEntry("Navigation Path " + name + " added", EntryType.General, "SYSTEM");
			}
			else Log.LogEntry("Creation failed for Navigation Path " + name, EntryType.Error, "SYSTEM");
			return newPath != null;
		}

		/// <summary>
		/// Removes a Navigation Path.
		/// </summary>
		/// <param name="name">The Name of the Path to remove.</param>
		/// <returns>True if the Path is removed successfully.</returns>
		public bool RemovePath(string name) {
			NavigationPath path = Find(name);
			if(path == null) return false;

			bool done = path.Provider.RemoveNavigationPath(name);
			if(done) {
				lock(paths) {
					paths.Remove(path);
				}
				Log.LogEntry("Navigation Path " + name + " removed", EntryType.General, "SYSTEM");
			}
			else Log.LogEntry("Deletion failed for Navigation Path " + name, EntryType.Error, "SYSTEM");
			return done;
		}

		/// <summary>
		/// Modifies a Navigation Path.
		/// </summary>
		/// <param name="name">The Name of the Path to modify.</param>
		/// <param name="pages">The list of Pages.</param>
		/// <returns>True if the Navigation Path is modified successfully.</returns>
		public bool ModifyPath(string name, List<PageInfo> pages) {
			NavigationPath path = Find(name);
			if(path == null) return false;

			NavigationPath newPath = path.Provider.ModifyNavigationPath(name, pages.ToArray());
			if(newPath != null) {
				lock(paths) {
					paths.Remove(path);
					paths.Add(newPath);
					paths.Sort(new NavigationPathComparer());
				}
				Log.LogEntry("Navigation Path " + name + " modified", EntryType.General, "SYSTEM");
			}
			else Log.LogEntry("Modification failed for Navigation Path " + name, EntryType.Error, "SYSTEM");
			return newPath != null;
		}

		/// <summary>
		/// Finds all the Navigation Paths that include a Page.
		/// </summary>
		/// <param name="page">The Page.</param>
		/// <returns>The list of Navigation Paths.</returns>
		public string[] PathsPerPage(PageInfo page) {
			List<string> result = new List<string>();
			for(int i = 0; i < paths.Count; i++) {
				if(paths[i].Pages.Contains(page.Name)) {
					result.Add(paths[i].Name);
				}
			}
			return result.ToArray();
		}

	}

}
